#pragma once

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <netdb.h>
#include <string.h>
#include <string>
#include <stdio.h>
#include <stdarg.h>
#include <sys/select.h>
#include <sys/time.h>
#include <arpa/inet.h>

// 消息体长度
#define MSGBODYLEN 4

namespace ys
{
        // 从格式字符串里解析内容
    bool GetStrFromXML(const char *formBuffer, const char *labelname, std::string &RtnValue)
    {
        // 标签开始格式
        std::string startOflabelname("<");
        startOflabelname.append(labelname);
        startOflabelname += ">";
        // 标签结束格式
        std::string endOflabelname("</");
        endOflabelname.append(labelname);
        endOflabelname += ">";

        // 定位标签
        const char *start = strstr(formBuffer, startOflabelname.c_str());
        const char *end = nullptr;
        if (start != nullptr)
        {
            end = strstr(formBuffer, endOflabelname.c_str());
        }

        if (start == nullptr || end == nullptr)
        {
            return false;
        }

        int m_ValueLen = end - start - startOflabelname.size(); // 内容长度

        RtnValue.assign(start + startOflabelname.size(), m_ValueLen); // 拼接内容
        return true;
    }
    bool GetStrFromXML(const char *formBuffer, const char *labelname, int &RtnValue)
    {
        std::string strTmp;
        // 调用重载版本
        if (GetStrFromXML(formBuffer, labelname, strTmp) == true)
        {
            RtnValue = atoi(strTmp.c_str());
            return true;
        }

        return false;
    }

    // 字符串形成 xml 格式
    void FormXML(std::string &message, const char *labelname)
    {
        std::string start("<");
        start.append(labelname);
        start.append(">");

        message.insert(0, start);

        message.append("</");
        message.append(labelname);
        message.append(">");
        return;
    }
    
    // 从已经准备好的socket中读取数据
    bool Readn(const int sockfd, char *buffer, const size_t n)
    {
        int nLeft = n;
        int nread = 0, idx = 0;

        // 循环读取n个字节
        while (nLeft > 0)
        {
            // 没有数据准备好会被阻塞
            if ((nread = recv(sockfd, buffer + idx, nLeft, 0)) <= 0)
            {
                return false;
            }
            idx += nread;
            nLeft -= nread;
        }

        return true;
    }

    // 接收socket的另一端发送过来的数据
    bool TcpRead(const int sockfd, char *buffer, int *ibuflen)
    {
        // socket连接无效
        if (sockfd == -1)
        {
            return false;
        }

        *ibuflen = 0;

        // 为解决TCP粘包和分包的问题
        // 报文组成为: 报文长度 + 报文体

        // 先读取前MSGBODYLEN个字节确定消息的长度
        if (Readn(sockfd, (char *)(ibuflen), MSGBODYLEN) == false)
        {
            return false;
        }

        *ibuflen = ntohl(*ibuflen); // 把网络字节序转换为主机字节序

        // 读取ibuflen个字节到buffer
        if (Readn(sockfd, buffer, (*ibuflen)) == false)
        {
            return false;
        }

        return true;
    }

    // 向已经准备好的socket中写入数据
    bool Writen(const int sockfd, const char *buffer, const size_t n)
    {
        int nLeft = n;
        int nwrite = 0, idx = 0;

        // 循环发送n个字节
        while (nLeft > 0)
        {
            // 发送缓冲区满时也可能被阻塞
            if ((nwrite = send(sockfd, buffer + idx, nLeft, 0)) <= 0)
            {
                return false;
            }
            idx += nwrite;
            nLeft -= nwrite;
        }

        return true;
    }

    // 向socket的另一端发送数据
    bool TcpWrite(const int sockfd, const char *buffer, const int ibuflen = 0)
    {
        // socket连接无效
        if (sockfd == -1)
        {
            return false;
        }

        int ilen = 0;
        // 如果长度为0(缺省值) 则使用字符串长度
        if (ibuflen == 0)
        {
            ilen = strlen(buffer);
        }
        else
        {
            ilen = ibuflen;
        }

        int ilenn = htonl(ilen); // 把主机字节序转换为网络字节序

        // 为解决TCP粘包和分包的问题
        // 报文组成为: 报文长度 + 报文体

        // 组合报文
        char strSendBuffer[ilen + 4];
        memset(strSendBuffer, 0, sizeof(strSendBuffer));
        memcpy(strSendBuffer, &ilenn, 4);
        memcpy(strSendBuffer + 4, buffer, ilen);
        // 发送数据
        if (Writen(sockfd, strSendBuffer, ilen + 4) == false)
        {
            return false;
        }

        return true;
    }

    // socket通信的客户端类
    class TcpClient
    {
    public:
        int _sockfd;    // 客户读的 socket
        char _ip[21];   // 服务端的 ip 地址
        int _port;      // 与服务端通信的端口
        int _buflen;    // 调用Read方法后, 接收到的报文大小, 单位: bytes

        // 构造与析构函数
        TcpClient()
            : _sockfd(-1), _port(0), _buflen(0)
        {
            memset(_ip, 0, sizeof(_ip));
        }

        ~TcpClient()
        {
            Close(); // 关闭连接
        }

        // 向服务端发起连接请求
        bool ConnectToServer(const char *ip, const int port)
        {
            // 如果上一个连接还存在就先close
            if (_sockfd!= -1)
            {
                close(_sockfd);
                _sockfd = -1;
            }

            strcpy(_ip, ip);
            _port = port;

            // 创建socket
            if ((_sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
            {
                perror("socket");
                return false;
            }

            // 创建通信套接字
            struct sockaddr_in serveraddr;
            memset(&serveraddr, 0, sizeof(serveraddr));

            serveraddr.sin_family = AF_INET;
            serveraddr.sin_port = htons(_port);
            serveraddr.sin_addr.s_addr = inet_addr(_ip);

            // 与服务器进行连接
            if (connect(_sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr))!= 0)
            {
                close(_sockfd);
                _sockfd = -1;
                perror("connect");
                return false;
            }

            return true;
        }

        // 接收服务端发送过来的数据
        bool Read(char *buffer, const int itimeout = 0)
        {
            // 连接不可用
            if (_sockfd == -1)
            {
                return false;
            }

            // 接收数据
            return (TcpRead(_sockfd, buffer, &_buflen));
        }

        // 向服务端发送数据
        bool Write(const char *buffer, const int ibuflen = 0)
        {
            // socket连接无效
            if (_sockfd == -1)
            {
                return false;
            }

            int ilen = ibuflen;
            if (ibuflen == 0)
            {
                ilen = strlen(buffer);
            }

            // 发送数据
            return (TcpWrite(_sockfd, buffer, ilen));
        }

        // 断开与服务器的连接
        void Close()
        {
            // 关闭连接并恢复变量状态
            if (_sockfd > 0)
            {
                close(_sockfd);
            }
            _sockfd = -1;
            memset(_ip, 0, sizeof(_ip));
            _port = 0;
            _buflen = 0;
        }
    };

    // socket通信的服务端类
    class TcpServer
    {
    private:
        struct sockaddr_in server;
        struct sockaddr_in client;

    public:
        int _listen_sockfd; // 服务端用于监听的 socket
        int _client_connfd; // 客户端连接上来的 socket
        int _buflen;       // 调用Read方法后, 接收到的报文大小, 单位: bytes

        // 构造和析构函数
        TcpServer()
            : _listen_sockfd(-1), _client_connfd(-1), _buflen(0)
        {
        }
        ~TcpServer()
        {
            CloseClient();
            CloseListen();
        }

        // 服务端初始化
        bool InitServer(const char *ip, const unsigned int port)
        {
            // 关闭上次未关闭的socket
            if (_listen_sockfd > 0)
            {
                close(_listen_sockfd);
                _listen_sockfd = -1;
            }

            // 创建监听套接字
            if ((_listen_sockfd = socket(AF_INET, SOCK_STREAM, 0)) <= 0)
            {
                perror("socket");
                return false;
            }

            // 设置端口地址重用
            int opt = 1;
            setsockopt(_listen_sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

            // 服务端信息
            memset(&server, 0, sizeof(server));
            server.sin_family = AF_INET;
            server.sin_addr.s_addr = inet_addr(ip);
            server.sin_port = htons(port);

            // 绑定ip和端口号
            if (bind(_listen_sockfd, (struct sockaddr *)&server, sizeof(server))!= 0)
            {
                perror("bind");
                CloseListen();
                return false;
            }

            // 开始监听
            if (listen(_listen_sockfd, 5)!= 0)
            {
                perror("listen");
                CloseListen();
                return false;
            }

            return true;
        }

        // 阻塞等待客户端的连接请求
        bool Accept()
        {
            if (_listen_sockfd == -1)
            {
                return false;
            }
            // 客户端信息
            memset(&client, 0, sizeof(client));
            socklen_t len = sizeof(client);

            // 获取新连接
            if ((_client_connfd = accept(_listen_sockfd, (struct sockaddr *)&client, &len)) < 0)
            {
                return false;
            }
            return true;
        }

        // 获取客户端的ip地址
        char *GetIP()
        {
            // 返回 客户端 ip 地址的 字符串
            return (inet_ntoa(client.sin_addr));
        }

        // 接收客户端发送过来的数据
        bool Read(char *buffer, const int itimeout = 0)
        {
            if (_client_connfd == -1)
            {
                return false;
            }

            // 接受数据
            return TcpRead(_client_connfd, buffer, &_buflen);
        }

        // 向客户端发送数据
        bool Write(const char *buffer, const int ibuflen = 0)
        {
            if (_client_connfd == -1)
            {
                return false;
            }

            int ilen = ibuflen;
            if (ibuflen == 0)
            {
                ilen = strlen(buffer);
            }

            // 发送数据
            return (TcpWrite(_client_connfd, buffer, ilen));
        }

        // 关闭监听的socket
        void CloseListen()
        {
            if (_listen_sockfd > 0)
            {
                close(_listen_sockfd);
                _listen_sockfd = -1;
            }
        }
        // 关闭客户端的socket
        void CloseClient()
        {
            if (_client_connfd > 0)
            {
                close(_client_connfd);
                _client_connfd = -1;
            }
        }
    };
}